package org.wheel.plugins.starter.jdbc.pool.dao;

import cn.hutool.core.lang.Pair;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.TypeUtil;
import cn.hutool.log.Log;
import cn.hutool.log.LogFactory;
import net.sf.jsqlparser.JSQLParserException;
import org.springframework.beans.factory.annotation.Autowired;
import org.wheel.module.core.enm.NormalExceptionEnum;
import org.wheel.module.core.entity.vo.Message;
import org.wheel.module.core.exception.NormalException;
import org.wheel.module.core.request.ApiParam;
import org.wheel.module.core.request.ParamPaging;
import org.wheel.module.core.util.ClassUtil;
import org.wheel.module.core.util.Page;
import org.wheel.plugins.starter.autoconfigure.DynamicDataSourceProperties;
import org.wheel.plugins.toolkit.jdbc.pool.util.LambdaSqlExecutionUtil;
import org.wheel.plugins.toolkit.jdbc.pool.util.SqlExecutionUtil;
import org.wheel.plugins.toolkit.jsqlparse.helper.ParamValueConvert;
import org.wheel.plugins.toolkit.sql.core.*;
import org.wheel.plugins.toolkit.sql.entity.BeanMeta;
import org.wheel.plugins.toolkit.sql.helper.FieldMeta;
import org.wheel.plugins.toolkit.util.sql.SqlConditionBuilder;

import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.List;

public class BaseDao<T> implements IBaseDao<T> {

    // 直接使用 LogFactory 创建日志对象，Hutool 会自动检测项目中的日志框架（优先级：Slf4j > Log4j > Logback > JDK Logging）
    private static final Log log = LogFactory.get();

    /* 让 Spring 自动注入 */
    @Autowired
    private DynamicDataSourceProperties conn;
    private final Class<T> entityClass;
    private final BeanMeta beanMeta;          // 你的实体元数据
    private final FieldMeta primaryKeyMeta;  // 主键字段元数据


//    public BaseDao(Class<T> entityClass, DynamicDataSourceProperties conn) {
//        this.entityClass = entityClass;
//        this.beanMeta = BeanMeta.of(entityClass);
//        this.primaryKeyMeta = beanMeta.getFields()
//                .stream()
//                .filter(FieldMeta::isPrimaryKey)
//                .findFirst()
//                .orElse(null);
//        this.conn = conn;
//    }

    public BaseDao() {
        // entityClass 找到接口basedao的泛型参数
        this.entityClass = (Class<T>) ((ParameterizedType) getClass()
                .getGenericSuperclass()).getActualTypeArguments()[0];
        this.beanMeta = BeanMeta.of(entityClass);
        this.primaryKeyMeta = beanMeta.getFields()
                .stream()
                .filter(FieldMeta::isPrimaryKey)
                .findFirst()
                .orElse(null);
    }

    /** 统一加逻辑删除条件，无侵入 */
    public BaseDao<T> applyLogicDelete(LambdaUpdateWrapper<T> wrapper, String opUserId) {
        BeanMeta meta = BeanMeta.of(wrapper.getEntityClass());
        if (meta.hasLogicDelete()) {
            wrapper.set(meta.getLogicDeleteField().getColumn(), 1);
//            wrapper.eq(meta.getLogicDeleteField().getColumn(), 0);
        }
        applyUpdateTime(wrapper, opUserId);
        return this;
    }
    public BaseDao<T> applyUpdateTime(LambdaUpdateWrapper<T> wrapper, String opUserId) {
        BeanMeta meta = BeanMeta.of(wrapper.getEntityClass());
        if (meta.hasUpdateBy() && StrUtil.isNotBlank(opUserId)){
            wrapper.set(meta.getUpdateByField().getColumn(), opUserId);
        }
        if (meta.hasCreateTime()) {
            wrapper.set(meta.getUpdateTimeField().getColumn(), new Date());
        }
        return this;
    }

    public BaseDao<T> applyInsertTime(LambdaInsertWrapper<T> wrapper, String opUserId) {
        BeanMeta meta = BeanMeta.of(wrapper.getEntityClass());
        if (meta.hasCreateBy() && StrUtil.isNotBlank(opUserId)) {
            wrapper.set(meta.getCreateByField().getColumn(), opUserId);
        }
        if (meta.hasCreateTime()) {
            wrapper.set(meta.getCreateTimeField().getColumn(), new Date());
        }
        if (meta.hasUpdateBy() && StrUtil.isNotBlank(opUserId)) {
            wrapper.set(meta.getUpdateByField().getColumn(), opUserId);
        }
        if (meta.hasUpdateTime()) {
            wrapper.set(meta.getUpdateTimeField().getColumn(), ParamValueConvert.convertValue(meta.getUpdateTimeField().getFieldType(), System.currentTimeMillis()+""));
        }
        if (meta.hasLogicDelete()) {
            wrapper.set(meta.getLogicDeleteField().getColumn(), ParamValueConvert.convertValue(meta.getLogicDeleteField().getFieldType(), "0"));
        }
        return this;
    }

    /* ===================== 通用 CRUD ===================== */

    /* ---------- 根据主键查询 ---------- */
    public Message selectByParam(LambdaQueryWrapper<T> wrapper, ApiParam apiParam){
        return selectByParam(wrapper, apiParam, entityClass);
    }
    /**
     * 根据条件查询
     * @param wrapper 查询条件
     * @param apiParam 参数
     * @param targetClass 查询结果类型
     * @return 查询结果
     */
    public <E> Message selectByParam(LambdaQueryWrapper<T> wrapper, ApiParam apiParam, Class<E> targetClass){
        try {
            SqlConditionBuilder.MergedSqlResult mergedResult = SqlConditionBuilder.buildConditionalSql(
                    wrapper.getSql(), wrapper.getParamMap(), apiParam, wrapper.getSqlModel());
            if(StrUtil.isBlank(mergedResult.sql())){
                return Message.error("查询异常：sql空了");
            }
            Long count = null;
            if(StrUtil.isNotBlank(mergedResult.countSql())){
                count = SqlExecutionUtil.getInstance().executeCountSqlQuery(
                        conn, mergedResult.countSql(), mergedResult.params(), mergedResult.sqlModel());
            }
            // 先注释掉SQL执行，只打印SQL和参数
            List<E> list = SqlExecutionUtil.getInstance().executeSqlQueryForList(
                    conn, mergedResult.sql(), mergedResult.params(), mergedResult.sqlModel(), targetClass);

            if(count != null && apiParam.getPaging() != null){
                ParamPaging paging = apiParam.getPaging();
                return Message.getMessgeInfo(paging.getPageNum(), paging.getPageSize(), count, list);
            }else{
                return Message.ok(list);
            }
        } catch (JSQLParserException e) {
            log.error("selectByParam error: {}", NormalException.getErrorInfoFromException(e));
            throw new NormalException(NormalExceptionEnum.SERVER_ERROR.getCode(), "查询SQL语句异常");
        }
    }

    public T selectById(Serializable id) {
        return selectById(id, entityClass);
    }
    public <E> E selectById(Serializable id, Class<E> targetClass) {
        assertPrimaryKey();
        LambdaQueryWrapper<T> wrapper = Wrappers.lambdaQuery(entityClass)
                .eq(primaryKeyMeta.getFieldName(), id);
        return LambdaSqlExecutionUtil.one(conn, wrapper, targetClass);
    }

    /* ---------- 批量主键查询 ---------- */
    public List<T> selectBatchIds(Collection<? extends Serializable> ids) {
        return selectBatchIds(ids, entityClass);
    }
    public <E> List<E> selectBatchIds(Collection<? extends Serializable> ids, Class<E> targetClass) {
        assertPrimaryKey();
        LambdaQueryWrapper<T> wrapper = Wrappers.lambdaQuery(entityClass)
                .in(primaryKeyMeta.getFieldName(), Collections.singletonList(ids));
        return LambdaSqlExecutionUtil.list(conn, wrapper, targetClass);
    }

    /* ---------- 分页查询 ---------- */
    public <E> Page<E> selectPage(Page<E> page, LambdaQueryWrapper<T> wrapper, Class<E> targetClass) {
        return LambdaSqlExecutionUtil.page(conn, wrapper,
                page.getPageNo(), page.getPageSize(), targetClass);
    }

    @Override
    public Page<T> selectPage(Page<T> page, LambdaQueryWrapper<T> wrapper) {
        return selectPage(page, wrapper, entityClass);
    }

    /* ---------- 列表查询 ---------- */
    public List<T> selectListAll(LambdaQueryWrapper<T> wrapper) {
        return LambdaSqlExecutionUtil.listAll(conn, wrapper, entityClass);
    }
    public <E> List<E> selectListAll(LambdaQueryWrapper<T> wrapper, Class<E> targetBean) {
        return LambdaSqlExecutionUtil.listAll(conn, wrapper, targetBean);
    }
    public List<T> selectList(LambdaQueryWrapper<T> wrapper) {
        return LambdaSqlExecutionUtil.list(conn, wrapper, entityClass);
    }
    public <E> List<E> selectList(LambdaQueryWrapper<T> wrapper, Class<E> targetClass) {
        return LambdaSqlExecutionUtil.list(conn, wrapper, targetClass);
    }

    /* ---------- 单条查询 ---------- */
    public T selectOne(LambdaQueryWrapper<T> wrapper) {
        return selectOne(wrapper, entityClass);
    }
    public <E> E selectOne(LambdaQueryWrapper<T> wrapper, Class<E> targetClass) {
        return LambdaSqlExecutionUtil.one(conn, wrapper, targetClass);
    }

    /* ---------- 计数 ---------- */
    public long selectCount(LambdaQueryWrapper<T> wrapper) {
        return LambdaSqlExecutionUtil.count(conn, wrapper);
    }

    /* ---------- 是否存在 ---------- */
    public boolean exists(LambdaQueryWrapper<T> wrapper) {
        return selectCount(wrapper) > 0;
    }

    /* ---------- 插入 ---------- */
    public int insert(LambdaInsertWrapper<T> wrapper){
        return LambdaSqlExecutionUtil.save(conn, wrapper);
    }

    public int insert(T entity) {
        return insert(entity, null);
    }

    public int insert(T entity, String opUserId) {
        LambdaInsertWrapper<T> iw = new LambdaInsertWrapper<>(entityClass).insert(entity);
        applyInsertTime(iw, opUserId);
        return LambdaSqlExecutionUtil.save(conn, iw);
    }

    /* ---------- 插入并返回主键 ---------- */
    public T insertAndGetId(T entity) {
        return insertAndGetId(entity, null);
    }

    public T insertAndGetId(T entity, String opUserId) {
        LambdaInsertWrapper<T> iw = new LambdaInsertWrapper<>(entityClass).insert(entity);
        applyInsertTime(iw, opUserId);
        return LambdaSqlExecutionUtil.saveAndGetId(conn, iw);
    }

    /* ---------- 批量插入 ---------- */
    public int[] insertBatch(Collection<T> entities) {
        return insertBatch(entities, null);
    }
    public int[] insertBatch(Collection<T> entities, String opUserId) {
        List<LambdaInsertWrapper<T>> iws = entities.stream()
                .map(entity -> new LambdaInsertWrapper<>(entityClass).insert(entity))
                .peek(b->applyInsertTime(b, opUserId))
                .toList();
        return LambdaSqlExecutionUtil.saveBatch(conn, iws);
    }

    /* ---------- 根据主键更新 ---------- */
    public int update(LambdaUpdateWrapper<T> wrapper){
        return LambdaSqlExecutionUtil.update(conn, wrapper);
    }
    @Override
    public int updateById(T entity, String opUserId) {
        return updateById(entity, true, opUserId);
    }
    /** 第二个参数值 ：是否忽略 null 值，true代表忽略，不更新对象中的null */
    public int updateById(T entity, boolean ignoreNull) {
        return updateById(entity, ignoreNull, null);
    }

    public int updateById(T entity, boolean ignoreNull, String opUserId){
        assertPrimaryKey();
        LambdaUpdateWrapper<T> uw = Wrappers.lambdaUpdate(entityClass)
                .eq(primaryKeyMeta.getFieldName(), getIdValue(entity))
                .update(entity, ignoreNull);
        this.applyUpdateTime(uw, opUserId);
        return LambdaSqlExecutionUtil.update(conn, uw);
    }
    /* ---------- 批量更新 ---------- */
    public int[] updateBatch(Collection<T> entities, boolean ignoreNull) {
        return this.updateBatch(entities, ignoreNull, null);
    }
    public int[] updateBatch(Collection<T> entities, String opUserId){
        return this.updateBatch(entities, true, opUserId);
    }
    public int[] updateBatch(Collection<T> entities, boolean ignoreNull, String opUserId) {
        List<LambdaUpdateWrapper<T>> uws = entities.stream()
                .map(entity -> Wrappers.lambdaUpdate(entityClass)
                        .eq(primaryKeyMeta.getFieldName(), getIdValue(entity))
                        .update(entity, ignoreNull))
                .peek(b->applyUpdateTime(b, opUserId))
                .toList();
        return LambdaSqlExecutionUtil.updateBatch(conn, uws);
    }

    public int[] updateBatch(Collection<Pair<T, Boolean>> entities) {
        List<LambdaUpdateWrapper<T>> uws = entities.stream()
                .map(entry -> Wrappers.lambdaUpdate(entityClass)
                        .eq(primaryKeyMeta.getFieldName(), getIdValue(entry.getKey()))
                        .update(entry.getKey(), entry.getValue()))
                .peek(b->applyUpdateTime(b, null))
                .toList();
        return LambdaSqlExecutionUtil.updateBatch(conn, uws);
    }
    /** 默认第二个参数值null：忽略 null 值 */
    public int updateById(T entity) {
        return updateById(entity, true);
    }

    public int[] updateById(Collection<T> entities) {
        return updateBatch(entities, true);
    }

    /* ---------- 根据主键软删除 ---------- */
    public int logicRemoveById(Serializable id, String opUserId){
        assertPrimaryKey();
        LambdaUpdateWrapper<T> uw = Wrappers.lambdaUpdate(entityClass)
                .eq(primaryKeyMeta.getFieldName(), id);
        applyLogicDelete(uw, opUserId);
        return LambdaSqlExecutionUtil.update(conn, uw);
    }

    public int deleteById(Serializable id) {
        assertPrimaryKey();
        LambdaDeleteWrapper<T> uw = Wrappers.lambdaDelete(entityClass)
                .eq(primaryKeyMeta.getFieldName(), id);
        return LambdaSqlExecutionUtil.delete(conn, uw);
    }

    /* ---------- 条件软删除 ---------- */
    public int delete(LambdaDeleteWrapper<T> wrapper) {
        return LambdaSqlExecutionUtil.delete(conn, wrapper);
    }
    public int logicRemove(LambdaUpdateWrapper<T> wrapper, String opUserId) {
        applyLogicDelete(wrapper, opUserId);
        return LambdaSqlExecutionUtil.update(conn, wrapper);
    }
    /* ---------- 批量删除 ---------- */
    public int[] deleteBatchIds(Collection<? extends Serializable> ids) {
        assertPrimaryKey();
        List<LambdaDeleteWrapper<T>> delWrappers = ids.stream()
                .map(id->Wrappers.lambdaDelete(entityClass)
                        .eq(primaryKeyMeta.getFieldName(), id)).toList();
        return LambdaSqlExecutionUtil.deleteBatch(conn, delWrappers);
    }

    public int[] logicDeleteBatchIds(Collection<? extends Serializable> ids, String opUserId) {
        assertPrimaryKey();
        List<LambdaUpdateWrapper<T>> uws = ids.stream()
                .map(id->Wrappers.lambdaUpdate(entityClass)
                        .eq(primaryKeyMeta.getFieldName(), id))
                .peek(b->applyLogicDelete(b, opUserId))
                .toList();
        return LambdaSqlExecutionUtil.updateBatch(conn, uws);
    }

    public int[] delete(Collection<LambdaDeleteWrapper<T>> wrappers) {
        return LambdaSqlExecutionUtil.deleteBatch(conn, wrappers);
    }

    public int[] logicDelete(Collection<LambdaUpdateWrapper<T>> wrappers, String opUserId) {
        List<LambdaUpdateWrapper<T>> uws = wrappers.stream()
                .peek(b->applyLogicDelete(b, opUserId))
                .toList();
        return LambdaSqlExecutionUtil.updateBatch(conn, uws);
    }

    /* ===================== 私有工具 ===================== */

    private void assertPrimaryKey() {
        if (primaryKeyMeta == null) {
            throw new IllegalStateException(
                    "实体 " + entityClass.getName() + " 未标记主键，无法执行基于主键的操作");
        }
    }

    private Serializable getIdValue(T entity) {
        // 1. 字段名
        String pkFieldName = primaryKeyMeta.getFieldName();   // 例如 "id"
        // 2. 用 Hutool 直接取值
        Object value = ReflectUtil.getFieldValue(entity, pkFieldName);
        if (value == null) {
            throw new IllegalArgumentException(
                    "实体 " + entityClass.getSimpleName() + " 的主键字段 " + pkFieldName + " 值为 null");
        }
        // 3. 强转
        if (!(value instanceof Serializable)) {
            throw new IllegalArgumentException(
                    "主键字段 " + pkFieldName + " 必须实现 java.io.Serializable");
        }
        return (Serializable) value;
    }
}